package com.tarena.lbs.article.web.dao.repository;

import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.sun.org.apache.bcel.internal.generic.RETURN;
import com.tarena.lbs.pojo.content.entity.ArticleSearchEntity;
import com.tarena.lbs.pojo.content.query.ArticleQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Repository
@Slf4j
public class ArticleRepository {
    //es的客户端api
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private ArticleESRepository esRepository;

    /**
     * 使用query里面的 非空属性拼接 bool字条件 不同属性类型 使用不同的查询方式
     * 比如 如果是标题 匹配相似 如果是状态 相等 如果是地理位置 范围查询
     * @param repositoryQuery
     * @return
     */
    public List<ArticleSearchEntity> search(ArticleQuery repositoryQuery) {
        //1.搜索请求
        SearchRequest request = new SearchRequest("lbs_article");
        //2.请求参数
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        assembleSearchSource(repositoryQuery, sourceBuilder);
        request.source(sourceBuilder);
        //3.调用请求 解析响应
        List<ArticleSearchEntity> entities=null;
        try{
            SearchResponse searchResponse=restHighLevelClient.search(request, RequestOptions.DEFAULT);
            SearchHit[] hits = searchResponse.getHits().getHits();
            if (hits!=null&hits.length>0){
                //检索结果非空
                entities=Arrays.stream(hits).map(hit->{
                    //从hit 中拿到json 反向解析成entity对象返回
                    return JSON.parseObject(hit.getSourceAsString(),ArticleSearchEntity.class);
                }).collect(Collectors.toList());
            }
        }catch (Exception e){
            e.printStackTrace();
            //打印日志error
        }
        return entities;
    }

    private void assembleSearchSource(ArticleQuery repositoryQuery, SearchSourceBuilder sourceBuilder) {
        //根据repositoryQuery中非空属性 拼接bool查询条件的字条件 交给sourceBuilder
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        if (StringUtils.isNotEmpty(repositoryQuery.getArticleTitle())){
            //1. 文章标题非空 需要组织bool字条件 模糊查询 es中是相似度查询使用matchquery
            query.must(QueryBuilders.matchQuery("articleTitle",repositoryQuery.getArticleTitle()));
        }
        if (repositoryQuery.getArticleCategoryId()!=null){
            //2. 文章分类非空 相等查询termQuery
            query.must(QueryBuilders.termQuery("articleCategoryId",repositoryQuery.getArticleCategoryId()));
        }
        //3.标签非空 判断term相等
        if (StringUtils.isNotEmpty(repositoryQuery.getArticleLabel())){
            query.must(QueryBuilders.termQuery("articleLabel",repositoryQuery.getArticleLabel()));
        }
        //4.文章状态 list 但是参数只可能有一个元素 term
        if (CollectionUtils.isNotEmpty(repositoryQuery.getArticleStatus())&&repositoryQuery.getArticleStatus().size()==1){
            query.must(QueryBuilders.termQuery("articleStatus",repositoryQuery.getArticleStatus().get(0)));
        }
        //5.source非空 term 只有后台才关心source 前台手机小程序 不关心source
        if (repositoryQuery.getSource()!=null){
            query.must(QueryBuilders.termQuery("source",repositoryQuery.getSource()));
        }
        //6.关联的活动id非空 term
        if (repositoryQuery.getActivityId()!=null){
            query.must(QueryBuilders.termQuery("activityId",repositoryQuery.getActivityId()));
        }
        //7.userId非空 查询该用户的文章数据
        if (repositoryQuery.getUserId()!=null){
            query.must(QueryBuilders.termQuery("userId",repositoryQuery.getUserId()));
        }
        //8.范围查询rangeQuery startTime下限 endTime上限
        if (repositoryQuery.getUpdateStartTime()!=null){
            query.must(QueryBuilders.rangeQuery("updateTime").gte(repositoryQuery.getUpdateStartTime()));
        }
        if (repositoryQuery.getUpdateEndTime()!=null){
            query.must(QueryBuilders.rangeQuery("updateTime").lte(repositoryQuery.getUpdateEndTime()));
        }
        //9.地理位置查询 中心点 lat lon 半径 100公里
        if (StringUtils.isNotBlank(repositoryQuery.getLatitude())&&StringUtils.isNotBlank(repositoryQuery.getLongitude())){
            GeoDistanceQueryBuilder geoQuery = QueryBuilders.geoDistanceQuery("location");
            geoQuery.point(Double.valueOf(repositoryQuery.getLatitude()),Double.valueOf(repositoryQuery.getLongitude()));
            geoQuery.distance(100D, DistanceUnit.KILOMETERS);
            query.must(geoQuery);
        }
        sourceBuilder.query(query);
        log.info("当前封装的查询条件:{}",sourceBuilder);
    }

    public void save(ArticleSearchEntity entity) {
        esRepository.save(entity);
    }
    //RestHighLevelClient 处理查询逻辑
    public List<ArticleSearchEntity> getArticleLabelsByLocation(ArticleQuery articleQuery) {
        //1.准备搜索的请求 对应的索引是文章索引 lbs_article
        SearchRequest request = new SearchRequest("lbs_article");
        //2.封装查询的参数 查询的逻辑 先查范围100公里 所有文章 再对文章集合使用标签值去重(keyword 是统计数据 )
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //2.1 地理距离查询
        GeoDistanceQueryBuilder query = QueryBuilders.geoDistanceQuery("location")
                .point(Double.valueOf(articleQuery.getLatitude()),
                        Double.valueOf(articleQuery.getLongitude()))
                .distance(100d, DistanceUnit.KILOMETERS);
        sourceBuilder.query(query);
        //2.2 基于标签去重
        sourceBuilder.collapse(new CollapseBuilder("articleLabel"));
        log.info("当前去重查询对象:{}",sourceBuilder);
        request.source(sourceBuilder);
        //3.发送请求 解析响应 hit文档对象中的source转化成entity集合数据组
        SearchResponse response=null;
        try{
            response=restHighLevelClient.search(request,RequestOptions.DEFAULT);
        }catch (Exception e){
            log.error("搜索查询文章标签集合出现异常",e);
        }
        if (response!=null){
            //从响应中拿到 文档的数组
            SearchHit[] hits = response.getHits().getHits();
            if (hits!=null&&hits.length>0){
                return Arrays.stream(hits).map(hit->{
                    //拿到hit中的sourcejson 转化entity
                    return JSON.parseObject(hit.getSourceAsString(),ArticleSearchEntity.class);
                }).collect(Collectors.toList());
            }
        }
        return null;
    }

    public ArticleSearchEntity getArticleById(Integer id) {
        return esRepository.findById(id).get();
    }
}
